package net.courier.remoting.provider.netty;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
import net.courier.remoting.client.ClientFactory;
import net.courier.remoting.client.RequestSendClient;

public class NettyClientFactory implements ClientFactory {
	private static final int DEFAULT_MAX_OBJECT_SIZE = 1048576;

	private int maxObjectSize = DEFAULT_MAX_OBJECT_SIZE;

	private Integer connectTimeoutMillis;
	private Boolean keepAlive = true;
	private Integer sendBufferSize;
	private Integer receiveBufferSize;
	private Integer socketTimeout;
	private Boolean autoRead;
	private Boolean tcpNodelay;

	public void setMaxObjectSize(int maxObjectSize) {
		this.maxObjectSize = maxObjectSize;
	}

	public void setConnectTimeoutMillis(int connectTimeoutMillis) {
		this.connectTimeoutMillis = connectTimeoutMillis;
	}

	public void setKeepAlive(boolean keepAlive) {
		this.keepAlive = keepAlive;
	}

	public void setSendBufferSize(int sendBufferSize) {
		this.sendBufferSize = sendBufferSize;
	}

	public void setReceiveBufferSize(int receiveBufferSize) {
		this.receiveBufferSize = receiveBufferSize;
	}

	public void setAutoRead(Boolean autoRead) {
		this.autoRead = autoRead;
	}

	public void setSocketTimeout(Integer socketTimeout) {
		this.socketTimeout = socketTimeout;
	}

	public void setTcpNodelay(Boolean tcpNodelay) {
		this.tcpNodelay = tcpNodelay;
	}

	public Bootstrap createBootstrap(EventLoopGroup group) {
		Bootstrap bootstrap = new Bootstrap();
		bootstrap.group(group).channel(NioSocketChannel.class);
		setOptions(bootstrap).handler(new ChannelInitializer<SocketChannel>() {
			@Override
			public void initChannel(SocketChannel ch) throws Exception {
				ch.pipeline().addLast("ObjectEncoder", new ObjectEncoder());
				ch.pipeline().addLast("ObjectDecoder",
						new ObjectDecoder(maxObjectSize, ClassResolvers.weakCachingResolver(null)));
			}
		});
		return bootstrap;
	}

	private Bootstrap setOptions(Bootstrap bootstrap) {
		bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectTimeoutMillis);
		bootstrap.option(ChannelOption.AUTO_READ, autoRead);
		bootstrap.option(ChannelOption.SO_KEEPALIVE, keepAlive);
		bootstrap.option(ChannelOption.SO_SNDBUF, sendBufferSize);
		bootstrap.option(ChannelOption.SO_RCVBUF, receiveBufferSize);
		bootstrap.option(ChannelOption.SO_TIMEOUT, socketTimeout);
		bootstrap.option(ChannelOption.TCP_NODELAY, tcpNodelay);
		return bootstrap;
	}

	@Override
	public RequestSendClient createClient(String host, int port) {
		EventLoopGroup group = new NioEventLoopGroup();
		Bootstrap bootstrap = createBootstrap(group);
		Channel channel = null;
		;
		try {
			channel = bootstrap.connect(host, port).sync().channel();
		} catch (InterruptedException e) {
			group.shutdownGracefully();
		}
		return new NettySendClient(group, channel);
	}

	@Override
	public void closeClient(RequestSendClient client) {
		client.close();
	}
}
